{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 投票法的思路"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;投票法是集成学习中常用的技巧，可以帮助我们提高模型的泛化能力，减少模型的错误率。举个例子，在航空航天领域，每个零件发出的电信号都对航空器的成功发射起到重要作用。如果我们有一个二进制形式的信号：\n",
    "\n",
    "11101100100111001011011011011\n",
    "\n",
    "在传输过程中第二位发生了翻转\n",
    "\n",
    "10101100100111001011011011011\n",
    "\n",
    "&emsp;&emsp;这导致的结果可能是致命的。一个常用的纠错方法是重复多次发送数据，并以少数服从多数的方法确定正确的传输数据。一般情况下，错误总是发生在局部，因此融合多个数据是降低误差的一个好方法，这就是投票法的基本思路。\n",
    "\n",
    "&emsp;&emsp;对于回归模型来说，投票法最终的预测结果是多个其他回归模型预测结果的平均值。\n",
    "\n",
    "&emsp;&emsp;对于分类模型，硬投票法的预测结果是多个模型预测结果中出现次数最多的类别，软投票对各类预测结果的概率进行求和，最终选取概率之和最大的类标签。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 投票法的原理分析"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;投票法是一种遵循少数服从多数原则的集成学习模型，通过多个模型的集成降低方差，从而提高模型的鲁棒性。在理想情况下，投票法的预测效果应当优于任何一个基模型的预测效果。\n",
    "\n",
    "&emsp;&emsp;投票法在回归模型与分类模型上均可使用：\n",
    "\n",
    "- 回归投票法：预测结果是所有模型预测结果的平均值。\n",
    "- 分类投票法：预测结果是所有模型种出现最多的预测结果。\n",
    "\n",
    "&emsp;&emsp;分类投票法又可以被划分为硬投票与软投票：\n",
    "\n",
    "- 硬投票：预测结果是所有投票结果最多出现的类。\n",
    "- 软投票：预测结果是所有投票结果中概率加和最大的类。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;下面我们使用一个例子说明硬投票：\n",
    "\n",
    "> 对于某个样本：\n",
    ">\n",
    "> 模型 1 的预测结果是 类别 A\n",
    ">\n",
    "> 模型 2 的预测结果是 类别 B\n",
    ">\n",
    "> 模型 3 的预测结果是 类别 B\n",
    "\n",
    "有2/3的模型预测结果是B，因此硬投票法的预测结果是B\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;同样的例子说明软投票：\n",
    "\n",
    "> 对于某个样本：\n",
    ">\n",
    "> 模型 1 的预测结果是 类别 A 的概率为 99%\n",
    ">\n",
    "> 模型 2 的预测结果是 类别 A 的概率为 49%\n",
    ">\n",
    "> 模型 3 的预测结果是 类别 A 的概率为 49%\n",
    "\n",
    "最终对于类别A的预测概率的平均是 (99 + 49 + 49) / 3 = 65.67%，因此软投票法的预测结果是A。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;从这个例子我们可以看出，软投票法与硬投票法可以得出完全不同的结论。相对于硬投票，软投票法考虑到了预测概率这一额外的信息，因此可以得出比硬投票法更加准确的预测结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;在投票法中，我们还需要考虑到不同的基模型可能产生的影响。理论上，基模型可以是任何已被训练好的模型。但在实际应用上，想要投票法产生较好的结果，需要满足两个条件：\n",
    "\n",
    "- 基模型之间的效果不能差别过大。当某个基模型相对于其他基模型效果过差时，该模型很可能成为噪声。\n",
    "- 基模型之间应该有较小的同质性。例如在基模型预测效果近似的情况下，基于树模型与线性模型的投票，往往优于两个树模型或两个线性模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "&emsp;&emsp;当投票合集中使用的模型能预测出清晰的类别标签时，适合使用硬投票。当投票集合中使用的模型能预测类别的概率时，适合使用软投票。软投票同样可以用于那些本身并不预测类成员概率的模型，只要他们可以输出类似于概率的预测分数值（例如支持向量机、k-最近邻和决策树）。\n",
    "\n",
    "&emsp;&emsp;投票法的局限性在于，它对所有模型的处理是一样的，这意味着所有模型对预测的贡献是一样的。如果一些模型在某些情况下很好，而在其他情况下很差，这是使用投票法时需要考虑到的一个问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 投票法的案例分析(基于sklearn，介绍pipe管道的使用以及voting的使用)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;Sklearn中提供了 [VotingRegressor](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.VotingRegressor.html) 与 [VotingClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.VotingClassifier.html) 两个投票方法。 &emsp;&emsp;这两种模型的操作方式相同，并采用相同的参数。使用模型需要提供一个模型列表，列表中每个模型采用Tuple的结构表示，第一个元素代表名称，第二个元素代表模型，需要保证每个模型必须拥有唯一的名称。\n",
    "\n",
    "&emsp;&emsp;例如这里，我们定义两个模型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.ensemble import VotingClassifier\n",
    "from sklearn.pipeline import make_pipeline\n",
    "from sklearn.preprocessing import StandardScaler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "models = [('lr',LogisticRegression()),('svm',SVC())]\n",
    "ensemble = VotingClassifier(estimators=models)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;有时某些模型需要一些预处理操作，我们可以为他们定义Pipeline完成模型预处理工作："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "models = [('lr',LogisticRegression()),('svm',make_pipeline(StandardScaler(),SVC()))]\n",
    "ensemble = VotingClassifier(estimators=models)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;模型还提供了voting参数让我们选择软投票或者硬投票："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "models = [('lr',LogisticRegression()),('svm',SVC())]\n",
    "ensemble = VotingClassifier(estimators=models, voting='soft')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;下面我们使用一个完整的例子演示投票法的使用：\n",
    "\n",
    "&emsp;&emsp;首先我们创建一个1000个样本，20个特征的随机数据集："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# test classification dataset\n",
    "from sklearn.datasets import make_classification\n",
    "# define dataset\n",
    "def get_dataset():\n",
    "    X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=2)\n",
    "    # summarize the dataset\n",
    "    return X,y"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们使用多个KNN模型作为基模型演示投票法，其中每个模型采用不同的邻居值K参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get a voting ensemble of models\n",
    "def get_voting():\n",
    "    # define the base models\n",
    "    models = list()\n",
    "    models.append(('knn1', KNeighborsClassifier(n_neighbors=1)))\n",
    "    models.append(('knn3', KNeighborsClassifier(n_neighbors=3)))\n",
    "    models.append(('knn5', KNeighborsClassifier(n_neighbors=5)))\n",
    "    models.append(('knn7', KNeighborsClassifier(n_neighbors=7)))\n",
    "    models.append(('knn9', KNeighborsClassifier(n_neighbors=9)))\n",
    "    # define the voting ensemble\n",
    "    ensemble = VotingClassifier(estimators=models, voting='hard')\n",
    "    return ensemble"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;然后，我们可以创建一个模型列表来评估投票带来的提升，包括KNN模型配置的每个独立版本和硬投票模型。下面的get_models()函数可以为我们创建模型列表进行评估。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get a list of models to evaluate\n",
    "def get_models():\n",
    "    models = dict()\n",
    "    models['knn1'] = KNeighborsClassifier(n_neighbors=1)\n",
    "    models['knn3'] = KNeighborsClassifier(n_neighbors=3)\n",
    "    models['knn5'] = KNeighborsClassifier(n_neighbors=5)\n",
    "    models['knn7'] = KNeighborsClassifier(n_neighbors=7)\n",
    "    models['knn9'] = KNeighborsClassifier(n_neighbors=9)\n",
    "    models['hard_voting'] = get_voting()\n",
    "    return models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;下面的evaluate_model()函数接收一个模型实例，并以分层10倍交叉验证三次重复的分数列表的形式返回。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "# evaluate a give model using cross-validation\n",
    "from sklearn.model_selection import cross_val_score   #Added by ljq\n",
    "def evaluate_model(model, X, y):\n",
    "    cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)\n",
    "    scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise')\n",
    "    return scores"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;然后，我们可以报告每个算法的平均性能，还可以创建一个箱形图和须状图来比较每个算法的精度分数分布。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from matplotlib import pyplot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      ">knn1 0.873 (0.030)\n",
      ">knn3 0.889 (0.038)\n",
      ">knn5 0.895 (0.031)\n",
      ">knn7 0.899 (0.035)\n",
      ">knn9 0.900 (0.033)\n",
      ">hard_voting 0.902 (0.034)\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# define dataset\n",
    "X, y = get_dataset()\n",
    "# get the models to evaluate\n",
    "models = get_models()\n",
    "# evaluate the models and store results\n",
    "results, names = list(), list()\n",
    "for name, model in models.items():\n",
    "    scores = evaluate_model(model, X, y)\n",
    "    results.append(scores)\n",
    "    names.append(name)\n",
    "    print('>%s %.3f (%.3f)' % (name, mean(scores), std(scores)))\n",
    "# plot model performance for comparison\n",
    "pyplot.boxplot(results, labels=names, showmeans=True)\n",
    "pyplot.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们得到的结果如下：    \n",
    ">knn1 0.873 (0.030)    \n",
    ">knn3 0.889 (0.038)    \n",
    ">knn5 0.895 (0.031)    \n",
    ">knn7 0.899 (0.035)    \n",
    ">knn9 0.900 (0.033)    \n",
    ">hard_voting 0.902 (0.034)    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;显然投票的效果略大于任何一个基模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;通过箱形图我们可以看到硬投票方法对交叉验证整体预测结果分布带来的提升。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## bagging的思路"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;与投票法不同的是，Bagging不仅仅集成模型最后的预测结果，同时采用一定策略来影响基模型训练，保证基模型可以服从一定的假设。在上一章中我们提到，希望各个模型之间具有较大的差异性，而在实际操作中的模型却往往是同质的，因此一个简单的思路是通过不同的采样增加模型的差异性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## bagging的原理分析"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;Bagging的核心在于自助采样(bootstrap)这一概念，即有放回的从数据集中进行采样，也就是说，同样的一个样本可能被多次进行采样。一个自助采样的小例子是我们希望估计全国所有人口年龄的平均值，那么我们可以在全国所有人口中随机抽取不同的集合（这些集合可能存在交集），计算每个集合的平均值，然后将所有平均值的均值作为估计值。\n",
    "\n",
    "&emsp;&emsp;首先我们随机取出一个样本放入采样集合中，再把这个样本放回初始数据集，重复K次采样，最终我们可以获得一个大小为K的样本集合。同样的方法， 我们可以采样出T个含K个样本的采样集合，然后基于每个采样集合训练出一个基学习器，再将这些基学习器进行结合，这就是Bagging的基本流程。\n",
    "\n",
    "&emsp;&emsp;对回归问题的预测是通过预测取平均值来进行的。对于分类问题的预测是通过对预测取多数票预测来进行的。Bagging方法之所以有效，是因为每个模型都是在略微不同的训练数据集上拟合完成的，这又使得每个基模型之间存在略微的差异，使每个基模型拥有略微不同的训练能力。\n",
    "\n",
    "&emsp;&emsp;Bagging同样是一种降低方差的技术，因此它在不剪枝决策树、神经网络等易受样本扰动的学习器上效果更加明显。在实际的使用中，加入列采样的Bagging技术对高维小样本往往有神奇的效果。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## bagging的案例分析(基于sklearn，介绍随机森林的相关理论以及实例)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;Sklearn为我们提供了 [BaggingRegressor](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.BaggingRegressor.html) 与 [BaggingClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.BaggingClassifier.html) 两种Bagging方法的API，我们在这里通过一个完整的例子演示Bagging在分类问题上的具体应用。这里两种方法的默认基模型是树模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;这里的树模型一般指决策树，它是一种树形结构，树的每个非叶子节点表示对样本在一个特征上的判断，节点下方的分支代表对样本的划分。决策树的建立过程是一个对数据不断划分的过程，每次划分中，首先要选择用于划分的特征，之后要确定划分的方案（类别/阈值）。我们希望通过划分，决策树的分支节点所包含的样本“纯度”尽可能地高。节点划分过程中所用的指标主要是信息增益和GINI系数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;信息增益衡量的是划分前后信息不确定性程度的减小。信息不确定程度一般使用信息熵来度量，其计算方式是：\n",
    "$$\n",
    "H(Y) = -\\sum{p_ilogp_i}\n",
    "$$\n",
    "&emsp;&emsp;其中i表示样本的标签，p表示该类样本出现的概率。当我们对样本做出划分之后，计算样本的条件熵：\n",
    "$$\n",
    "H(Y|X) = -\\sum_{x\\in X}p(X=x){H(Y|X=x)}\n",
    "$$\n",
    "\n",
    "&emsp;&emsp;其中x表示用于划分的特征的取值。信息增益定义为信息熵与条件熵的差值：\n",
    "$$\n",
    "IG = H(Y) - H(Y|X)\n",
    "$$\n",
    "\n",
    "&emsp;&emsp;信息增益IG越大，说明使用该特征划分数据所获得的信息量变化越大，子节点的样本“纯度”越高。\n",
    "\n",
    "&emsp;&emsp;同样的，我们也可以利用Gini指数来衡量数据的不纯度，计算方法如下：\n",
    "$$\n",
    "Gini = 1 - \\sum{p_i^2}\n",
    "$$\n",
    "&emsp;&emsp;当我们对样本做出划分后，计算划分后的Gini指数：\n",
    "$$\n",
    "Gini_x = \\sum_{x\\in X}p(X=x)[1 - \\sum{p_i^2}]\n",
    "$$\n",
    "&emsp;&emsp;一般来说，我们选择使得划分后Gini指数最小的特征（注意这里是直接根据Gini指数进行判断，而并非其变化量）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;下方给出了决策树的一个例子，我们要训练一个模型，根据天气、温度和风力等级来判断是否打网球。    \n",
    "![image-20210411151227186](./DecisionTree.png)\n",
    "\n",
    "首先我们通过计算信息增益或Gini指数确定了首先根据天气情况对样本进行划分，之后对于每个分支，继续考虑除天气之外的其他特征，直到样本的类别被完全分开，所有特征都已使用，或达到树的最大深度为止。\n",
    "\n",
    "Bagging的一个典型应用是随机森林。顾名思义，“森林”是由许多“树”bagging组成的。在具体实现上，用于每个决策树训练的样本和构建决策树的特征都是通过随机采样得到的，随机森林的预测结果是多个决策树输出的组合（投票）。随机森林的示意图如下：\n",
    "\n",
    "![image-20210411161043251](./RandomForest.png)\n",
    "\n",
    "\n",
    "\n",
    "下面我们使用sklearn来实现基于决策树方法的bagging策略。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们创建一个含有1000个样本20维特征的随机分类数据集："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1000, 20) (1000,)\n"
     ]
    }
   ],
   "source": [
    "# test classification dataset\n",
    "from sklearn.datasets import make_classification\n",
    "# define dataset\n",
    "X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, \n",
    "                           n_redundant=5, random_state=5)\n",
    "# summarize the dataset\n",
    "print(X.shape, y.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;我们将使用重复的分层k-fold交叉验证来评估该模型，一共重复3次，每次有10个fold。我们将评估该模型在所有重复交叉验证中性能的平均值和标准差。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy: 0.851 (0.041)\n"
     ]
    }
   ],
   "source": [
    "# evaluate bagging algorithm for classification\n",
    "from numpy import mean\n",
    "from numpy import std\n",
    "from sklearn.datasets import make_classification\n",
    "from sklearn.model_selection import cross_val_score\n",
    "from sklearn.model_selection import RepeatedStratifiedKFold\n",
    "from sklearn.ensemble import BaggingClassifier\n",
    "# define dataset\n",
    "X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, n_redundant=5, random_state=5)\n",
    "# define the model\n",
    "model = BaggingClassifier()\n",
    "# evaluate the model\n",
    "cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)\n",
    "n_scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise')\n",
    "# report performance\n",
    "print('Accuracy: %.3f (%.3f)' % (mean(n_scores), std(n_scores)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "&emsp;&emsp;最终模型的效果是Accuracy: 0.856 标准差0.037"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
